-- This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.

local ROT = require 'rot'

local game = {}

game.ROT = ROT

game.rng = ROT.RNG
game.quitting = false
game.messages = {}
game.messageRepeat = {}

function game:log(...)
    local last = #game.messages
    local value = table.concat({ ... })
    if game.messages[last] == value then
        game.messageRepeat[last] = (game.messageRepeat[last] or 1) + 1
    else
        game.messages[last + 1] = value
    end
end

function game:loadConfig(path)
    local env = {}
    assert(loadfile(path, 't', env))()
    return env
end

function game:initDisplay()
    local h, w = (io.popen 'stty size'):read():match '(%d+)%s+(%d+)'
    game:log('Display size: ', w, ' x ', h)
    self.display = ROT.Display(tonumber(w), tonumber(h))
end

function game:refresh()
    self.state:update()
    self.display:draw()
    io.stdout:flush()
end

function game:run ()
    game.keys = self:loadConfig('keys.lua')

    game.commandByKey = {}

    for k, v in pairs(game.keys) do game.commandByKey[v] = k end

    game:initDisplay()

    game:pushState('launch')
    game.turns = 1

    os.execute 'stty -echo cbreak' -- echo off, cbreak on
    io.stdout:write '\27]0;Splat!\7' -- set terminal title
    io.stdout:write '\27[2J\27[?25l' -- clear screen, hide cursor

    repeat
        local time = os.clock() + 0.015
        self:refresh()
        repeat until os.clock() > time
        game.state:keypressed(io.stdin:read(1))
    until game.quitting

    io.stdout:write '\n\n\27[?25h' -- show cursor
    os.execute 'stty echo -cbreak' -- echo on, cbreak off
end

function game:prompt(message, info)
    os.execute 'stty echo -cbreak'
    self.prompting = true
    self.state:update()
    self.display:draw()
    if info then
        io.stdout:write('\27[' .. game.display.height .. ';0H\27[2K\27[2m' .. info)
    end
    io.stdout:write('\27[' .. (game.display.height-1) .. ';0H\27[2K\27[2m') -- move cursor, clear line, dim
    io.stdout:write(message)
    io.stdout:write '\27[0m\27[?25h' -- reset attributes, show cursor
    local response = io.stdin:read()
    os.execute 'stty -echo cbreak'
    io.stdout:write '\27[?25l' -- hide cursor
    self.prompting = false
    self.state:update()
    self.display:draw()
    return response ~= '' and response or nil
end

function game:roll(dice, sides, bonus)
    local sum = 0
    for i = 1, dice or 1 do
        sum = sum + self.rng:random(1, sides or 6)
    end
    return sum + (bonus or 0)
end

function game:pushState(name, ...)
    local prev = self.state
    self.state = require('game.state.' .. name)(self, prev, ...)
    self.state._previousState = prev
end

function game:popState(...)
    self.state = self.state._previousState
    self.state._previousState = nil
end

pcall(function()
    local signal = require 'signal' -- https://github.com/bcnjr5/lua-signal

    -- remake display on terminal resize
    signal.sethandler(signal.signum.SIGWINCH, function() game:initDisplay() end)
end)

game:run()
